package org.piax.rospubsub;

import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 * 特定のトピックに対応するTCPポートを開き、ROS 側から DTN への Publish を仲立ちするクラス
 */
public class PublishProxy {
    private String topic = null;
    private ADBBridge adbBridge = null;
    private int bufSize = -1;

    private ServerSocketChannel ssoc = null;
    private Thread acceptThread = null;

    /**
     * コンストラクタ
     * @param topic トピック名
     * @param adb_bridge Android との通信路
     * @param buf_size バッファサイズ
     * @throws IOException 待ち受けソケットを開けない場合
     */
    public PublishProxy(String topic, ADBBridge adb_bridge, int buf_size) throws IOException {
        if (topic == null || "".equals(topic))
            throw new IllegalArgumentException("topic should not be null or empty");
        if (adb_bridge == null)
            throw new IllegalArgumentException("adb_bridge should not be null");
        if (buf_size <= 0)
            throw new IllegalArgumentException("buf_size should be a positive number");

        this.topic = topic;
        this.adbBridge = adb_bridge;
        this.bufSize = buf_size;

        // クライアントからの要求を受けるソケットを開く
        ssoc = ServerSocketChannel.open();
        ssoc.socket().bind(new InetSocketAddress(0));   // 空いているポート番号を自動選択

        acceptThread = new AcceptThread();
        acceptThread.start();
    }

    /**
     * Publish 受け付けポート番号を取得する。
     * @return 受け付けポート番号
     */
    public int getLocalPort() {
        return ssoc.socket().getLocalPort();
    }

    private class AcceptThread extends Thread {
        @Override
        public void run() {
            try {
                SocketChannel cSocket = ssoc.accept();
                int port = cSocket.socket().getLocalPort();
                System.out.println("PublishProxy$AcceptThread accepted. port : " + port);
                // クライアントからのメッセージを待ち、受け取ったメッセージをADBBridgeに渡す
                ByteBuffer buf = ByteBuffer.allocate(bufSize);
                cSocket.read(buf);
                cSocket.close();

                if (adbBridge.isConnected()) {
                    buf.flip();
                    byte[] data = new byte[buf.remaining()];
                    buf.get(data);
                    ADBBridgeMessage msg = new ADBBridgeMessage(topic, data);
                    adbBridge.send(msg);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
